package com.ruoyi.activiti.service.impl;

import com.ruoyi.activiti.config.ICustomProcessDiagramGenerator;
import com.ruoyi.activiti.config.WorkflowConstants;
import com.ruoyi.activiti.domain.BizTodoItem;
import com.ruoyi.activiti.mapper.BizTodoItemMapper;
import com.ruoyi.activiti.service.IBizTodoItemService;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.activiti.domain.HistoricActivity;
import com.ruoyi.activiti.service.IProcessService;
import com.ruoyi.framework.util.ShiroUtils;
import com.ruoyi.system.domain.SysUser;
import com.ruoyi.system.mapper.SysUserMapper;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class ProcessServiceImpl implements IProcessService {
  protected final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);

  @Autowired
  private RuntimeService runtimeService;
  @Autowired
  private IdentityService identityService;
  @Autowired
  private TaskService taskService;
  @Autowired
  private HistoryService historyService;
  @Autowired
  private SysUserMapper userMapper;

  @Autowired
  private FormService  formService;

  @Autowired
  private BizTodoItemMapper bizTodoItemMapper;

  @Override
  public ProcessInstance submitApply(String applyUserId, String businessKey, String itemName, String itemConent, String processDefinitionKey, Map<String, Object> variables) {
    // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
    identityService.setAuthenticatedUserId(applyUserId);
    // 启动流程时设置业务 key
    ProcessInstance instance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
    // 下一节点处理人待办事项
//    bizTodoItemService.insertTodoItem(instance.getProcessInstanceId(), itemName, itemConent, businessType);
    return instance;
  }

  @Override
  public List<Task> findTodoTasks(String userId, String key) {
    List<Task> tasks = new ArrayList<Task>();
    // 根据当前人的ID查询
    List<Task> todoList = taskService
      .createTaskQuery()
      .processDefinitionKey(key)
      .taskAssignee(userId)
      .list();
    // 根据当前人未签收的任务
    List<Task> unsignedTasks = taskService
      .createTaskQuery()
      .processDefinitionKey(key)
      .taskCandidateUser(userId)
      .list();
    // 合并
    tasks.addAll(todoList);
    tasks.addAll(unsignedTasks);
    return tasks;
  }

  @Override
  public List<HistoricTaskInstance> findDoneTasks(String userId, String key) {
    List<HistoricTaskInstance> list = historyService
      .createHistoricTaskInstanceQuery()
      .processDefinitionKey(key)
      .taskAssignee(userId)
      .finished()
      .orderByHistoricTaskInstanceEndTime()
      .desc()
      .list();
    return list;
  }

  @Override
  public void complete(
    String plat,
    String loginName,
    String taskId,
    String instanceId,
    String itemName,
    String itemContent,
    String module,
    Map<String, Object> variables,
    HttpServletRequest request) {
    Enumeration<String> parameterNames = request.getParameterNames();
    String comment = null;          // 批注
    boolean aggree = true;
    try {
      if (!StringUtils.isEmpty(plat) && plat.equalsIgnoreCase("mobile")) {
        comment = (String) variables.get("comment");
        Integer approved = (Integer) variables.get("approved");
        if (StringUtils.isNotEmpty(comment)) {
          identityService.setAuthenticatedUserId(loginName);
          if (approved == 1) {
            comment = "【同意】" + comment;
          } else if (approved == -1) {
            comment = "【拒绝】" + comment;
          } else if (approved == 0) {
            comment = "【驳回】" + comment;
          }else if (approved == -2) {
            comment = "[废弃】" + comment;
          }
        }
      } else {
        while (parameterNames.hasMoreElements()) {
          String parameterName = parameterNames.nextElement();
          if (parameterName.startsWith("p_")) {
            // 参数结构：p_B_name，p为参数的前缀，B为类型，name为属性名称
            String[] parameter = parameterName.split("_");
            if (parameter.length == 3) {
              String paramValue = request.getParameter(parameterName);
              Object value = paramValue;
              if (parameter[1].equals("B")) {
                value = BooleanUtils.toBoolean(paramValue);
                aggree = (boolean) value;
              } else if (parameter[1].equals("DT")) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                value = sdf.parse(paramValue);
              } else if (parameter[1].equals("COM")) {
                comment = paramValue;
              }
              variables.put(parameter[2], value);
            } else {
              throw new RuntimeException("invalid parameter for activiti variable: " + parameterName);
            }
          }
        }
        if (StringUtils.isNotEmpty(comment)) {
          identityService.setAuthenticatedUserId(loginName);
          comment = aggree ? "【同意】" + comment : "【拒绝】" + comment;
        }
      }

      taskService.addComment(taskId, instanceId, comment == null ? "重新调整" : comment);
      // 被委派人处理完成任务
      // p.s. 被委托的流程需要先 resolved 这个任务再提交。
      // 所以在 complete 之前需要先 resolved
      // resolveTask() 要在 claim() 之前，不然 act_hi_taskinst 表的 assignee 字段会为 null
      taskService.resolveTask(taskId, variables);
      // 只有签收任务，act_hi_taskinst 表的 assignee 字段才不为 null
      taskService.claim(taskId, loginName);
      taskService.complete(taskId, variables);
    } catch (Exception e) {
      logger.error("error on complete task {}, variables={}", new Object[]{taskId, variables, e});
    }
  }


  @Override
  public List<HistoricActivity> selectHistoryList(String processInstanceId, HistoricActivity historicActivity) {
//        PageDomain pageDomain = TableSupport.buildPageRequest();
//        Integer pageNum = pageDomain.getPageNum();
//        Integer pageSize = pageDomain.getPageSize();
    List<HistoricActivity> activityList = new ArrayList<>();
    HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();
    if (StringUtils.isNotBlank(historicActivity.getAssignee())) {
      query.taskAssignee(historicActivity.getAssignee());
    }
    if (StringUtils.isNotBlank(historicActivity.getActivityName())) {
      query.activityName(historicActivity.getActivityName());
    }
    List<HistoricActivityInstance> list = query.processInstanceId(processInstanceId)
      .activityType("userTask")
      .finished()
      .orderByHistoricActivityInstanceStartTime()
      .asc()
      .list();
//                .listPage((pageNum - 1) * pageSize, pageNum * pageSize);
    list.forEach(instance -> {
      HistoricActivity activity = new HistoricActivity();
      BeanUtils.copyProperties(instance, activity);
      String taskId = instance.getTaskId();
      List<Comment> comment = taskService.getTaskComments(taskId, "comment");
      if (!CollectionUtils.isEmpty(comment)) {
        activity.setComment(comment.get(0).getFullMessage());
      }
      SysUser sysUser = userMapper.selectUserByLoginName(instance.getAssignee());
      if (sysUser != null) {
        activity.setAssigneeName(sysUser.getUserName());
      }
      activityList.add(activity);
    });
    return activityList;
  }

  @Override
  public void delegate(String taskId, String fromUser, String delegateToUser) {
    taskService.delegateTask(taskId, delegateToUser);
  }

  @Override
  public void cancelApply(String instanceId, String deleteReason) {
    // 执行此方法后未审批的任务 act_ru_task 会被删除，流程历史 act_hi_taskinst 不会被删除，并且流程历史的状态为finished完成
    runtimeService.deleteProcessInstance(instanceId, deleteReason);
  }

  @Override
  public void suspendOrActiveApply(String instanceId, String suspendState) {
    if ("1".equals(suspendState)) {
      // 当流程实例被挂起时，无法通过下一个节点对应的任务id来继续这个流程实例。
      // 通过挂起某一特定的流程实例，可以终止当前的流程实例，而不影响到该流程定义的其他流程实例。
      // 激活之后可以继续该流程实例，不会对后续任务造成影响。
      // 直观变化：act_ru_task 的 SUSPENSION_STATE_ 为 2
      runtimeService.suspendProcessInstanceById(instanceId);
    } else if ("2".equals(suspendState)) {
      runtimeService.activateProcessInstanceById(instanceId);
    }
  }

  @Override
  public String findBusinessKeyByInstanceId(String instanceId) {
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
    if (processInstance == null) {
      HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
        .processInstanceId(instanceId)
        .singleResult();
      return historicProcessInstance.getBusinessKey();
    } else {
      return processInstance.getBusinessKey();
    }
  }

  @Autowired
  private RepositoryService repositoryService;

  @Autowired
  private ProcessEngine processEngine;

  /**
   * 获取流程图像，已执行节点和流程线高亮显示
   */
  public void getActivitiProccessImage(String pProcessInstanceId, HttpServletResponse response) {
    //logger.info("[开始]-获取流程图图像");
    try {
      //  获取历史流程实例
      HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
        .processInstanceId(pProcessInstanceId).singleResult();

      if (historicProcessInstance == null) {
        //throw new BusinessException("获取流程实例ID[" + pProcessInstanceId + "]对应的历史流程实例失败！");
      } else {
        // 获取流程定义
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService)
          .getDeployedProcessDefinition(historicProcessInstance.getProcessDefinitionId());

        // 获取流程历史中已执行节点，并按照节点在流程中执行先后顺序排序
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
          .processInstanceId(pProcessInstanceId).orderByHistoricActivityInstanceId().asc().list();

        // 已执行的节点ID集合
        List<String> executedActivityIdList = new ArrayList<String>();
        int index = 1;
        //logger.info("获取已经执行的节点ID");
        for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
          executedActivityIdList.add(activityInstance.getActivityId());

          //logger.info("第[" + index + "]个已执行节点=" + activityInstance.getActivityId() + " : " +activityInstance.getActivityName());
          index++;
        }

        BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());

        // 已执行的线集合
        List<String> flowIds = new ArrayList<String>();
        // 获取流程走过的线 (getHighLightedFlows是下面的方法)
        flowIds = getHighLightedFlows(bpmnModel, processDefinition, historicActivityInstanceList);

//                // 获取流程图图像字符流
//                ProcessDiagramGenerator pec = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
//                //配置字体
//                InputStream imageStream = pec.generateDiagram(bpmnModel, "png", executedActivityIdList, flowIds,"宋体","微软雅黑","黑体",null,2.0);

        Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(pProcessInstanceId).list()
          .stream().map(e -> e.getActivityId()).collect(Collectors.toSet());

        ICustomProcessDiagramGenerator diagramGenerator = (ICustomProcessDiagramGenerator) processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
        InputStream imageStream = diagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList,
          flowIds, "宋体", "宋体", "宋体", null, 1.0, new Color[]{WorkflowConstants.COLOR_NORMAL, WorkflowConstants.COLOR_CURRENT}, currIds);

        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
          os.write(buffer, 0, bytesRead);
        }
        os.close();
        imageStream.close();
      }
      //logger.info("[完成]-获取流程图图像");
    } catch (Exception e) {
      System.out.println(e.getMessage());
      //logger.error("【异常】-获取流程图失败！" + e.getMessage());
      //throw new BusinessException("获取流程图失败！" + e.getMessage());
    }
  }

  private List<String> getHighLightedFlows(BpmnModel bpmnModel, ProcessDefinitionEntity processDefinitionEntity, List<HistoricActivityInstance> historicActivityInstances) {
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小时制
    List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId

    for (int i = 0; i < historicActivityInstances.size() - 1; i++) {
      // 对历史流程节点进行遍历
      // 得到节点定义的详细信息
      FlowNode activityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());


      List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
      FlowNode sameActivityImpl1 = null;

      HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
      HistoricActivityInstance activityImp2_;

      for (int k = i + 1; k <= historicActivityInstances.size() - 1; k++) {
        activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点

        if (activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
          df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime()))) //都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
        {

        } else {
          sameActivityImpl1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到紧跟在后面的一个节点
          break;
        }

      }
      sameStartTimeNodes.add(sameActivityImpl1); // 将后面第一个节点放在时间相同节点的集合里
      for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
        HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
        HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点

        if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime()))) {// 如果第一个节点和第二个节点开始时间相同保存
          FlowNode sameActivityImpl2 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
          sameStartTimeNodes.add(sameActivityImpl2);
        } else {// 有不相同跳出循环
          break;
        }
      }
      List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows(); // 取出节点的所有出去的线

      for (SequenceFlow pvmTransition : pvmTransitions) {// 对所有的线进行遍历
        FlowNode pvmActivityImpl = (FlowNode) bpmnModel.getMainProcess().getFlowElement(pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
        if (sameStartTimeNodes.contains(pvmActivityImpl)) {
          highFlows.add(pvmTransition.getId());
        }
      }

    }
    return highFlows;

  }

  @Override
  public void readResource(String pProcessInstanceId, HttpServletResponse response) throws Exception {
    // 设置页面不缓存
    response.setHeader("Pragma", "No-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);

    String processDefinitionId = "";
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
    if (processInstance == null) {
      HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(pProcessInstanceId).singleResult();
      processDefinitionId = historicProcessInstance.getProcessDefinitionId();
    } else {
      processDefinitionId = processInstance.getProcessDefinitionId();
    }
    ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
    ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();

    String resourceName = pd.getDiagramResourceName();

    if (resourceName.endsWith(".png") && StringUtils.isEmpty(pProcessInstanceId) == false) {
      getActivitiProccessImage(pProcessInstanceId, response);
      //ProcessDiagramGenerator.generateDiagram(pde, "png", getRuntimeService().getActiveActivityIds(processInstanceId));
    } else {
      // 通过接口读取
      InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);

      // 输出资源内容到相应对象
      byte[] b = new byte[1024];
      int len = -1;
      while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
        response.getOutputStream().write(b, 0, len);
      }
    }
  }

  //////////////////////////////////////////////////////////////////////////
  public void revoke(String businessKey, String loginName) throws Exception {
    Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
    if (task == null) {
      throw new RuntimeException("流程未启动或已执行完成，无法撤回");
    }
    List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
      .processInstanceBusinessKey(businessKey)
      .orderByTaskCreateTime()
      .asc()
      .list();
    String myTaskId = null;
    HistoricTaskInstance myTask = null;
    for (HistoricTaskInstance hti : htiList) {
      if (loginName.equals(hti.getAssignee())) {
        myTaskId = hti.getId();
        myTask = hti;
        break;
      }
    }
    if (null == myTaskId) {
      throw new RuntimeException("该任务非当前用户提交，无法撤回");
    }

    String processDefinitionId = myTask.getProcessDefinitionId();
    ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
    BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

    //变量
//  Map<String, VariableInstance> variables = runtimeService.getVariableInstances(currentTask.getExecutionId());
    String myActivityId = null;
    List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
      .executionId(myTask.getExecutionId()).finished().list();
    for (HistoricActivityInstance hai : haiList) {
      if (myTaskId.equals(hai.getTaskId())) {
        myActivityId = hai.getActivityId();
        break;
      }
    }
    FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);


    Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
    String activityId = execution.getActivityId();
    logger.warn("-------&gt;&gt; activityId:" + activityId);
    FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

    //记录原活动方向
    List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
    oriSequenceFlows.addAll(flowNode.getOutgoingFlows());

    //清理活动方向
    flowNode.getOutgoingFlows().clear();
    //建立新方向
    List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
    SequenceFlow newSequenceFlow = new SequenceFlow();
    newSequenceFlow.setId("newSequenceFlowId");
    newSequenceFlow.setSourceFlowElement(flowNode);
    newSequenceFlow.setTargetFlowElement(myFlowNode);
    newSequenceFlowList.add(newSequenceFlow);
    flowNode.setOutgoingFlows(newSequenceFlowList);

    Authentication.setAuthenticatedUserId(loginName);
    taskService.addComment(task.getId(), task.getProcessInstanceId(), "驳回");

    Map<String, Object> currentVariables = new HashMap<String, Object>();
    currentVariables.put("applier", loginName);
    //完成任务
    taskService.complete(task.getId(), currentVariables);
    //恢复原方向
    flowNode.setOutgoingFlows(oriSequenceFlows);
  }

  public List<String> taskToDoUserList(String instanceId){
    List<String>todoUserList=new ArrayList<>();
    List<Task> taskList = taskService.createTaskQuery().processInstanceId(instanceId).includeProcessVariables().active().list();
    for (Task task: taskList) {
      String assignee = task.getAssignee();
      if(assignee !=null){
        todoUserList.add(assignee);
        return todoUserList;
      }else{
        boolean b = hasProFlag(task);
        List<String> todoUserIdList=null;
        if(b){
          Map<String, Object> processVariables = task.getProcessVariables();
          String projectId = (String)processVariables.get("projectId");

          todoUserIdList= bizTodoItemMapper.selectTodoUserListByTaskId2(task.getId(),projectId);
          if (!CollectionUtils.isEmpty(todoUserIdList)) {
            todoUserList.addAll(todoUserIdList);
          }else{
            String todoUserId = bizTodoItemMapper.selectTodoUserByTaskId2(task.getId(),projectId);
            todoUserList.add(todoUserId);
          }
        }else{
          todoUserIdList= bizTodoItemMapper.selectTodoUserListByTaskId1(task.getId());
          if (!CollectionUtils.isEmpty(todoUserIdList)) {
            todoUserList.addAll(todoUserIdList);
          }else{
            String todoUserId = bizTodoItemMapper.selectTodoUserByTaskId1(task.getId());
            todoUserList.add(todoUserId);
          }
        }
      }

    }
    return todoUserList;
  }
  public boolean hasProFlag(Task task){
    TaskFormData taskFormData = formService.getTaskFormData(task.getId());
    List<FormProperty> formProperties = taskFormData.getFormProperties();
    if(formProperties!=null && formProperties.size() > 0){
      for(FormProperty formProperty:formProperties){
          if(formProperty.getId().equalsIgnoreCase("projectFlag")){
            return true;
          }
      }
    }
    return false;
  }
}
